# To change this template, choose Tools | Templates
# and open the template in the editor.

require 'java'
require 'sudoku'
require 'cell'
require 'sudoku_reader'
require 'sudoku_solver'
require 'rational'

include_class('javax.swing.JFrame')
include_class('javax.swing.JPanel')
include_class('javax.swing.JToolBar')
include_class('javax.swing.JLabel')
include_class('javax.swing.JSlider')
include_class('javax.swing.BorderFactory')
include_class('javax.swing.AbstractAction')
include_class('java.awt.BorderLayout')
include_class('java.awt.GridLayout')
include_class('java.awt.FlowLayout')
include_class('java.awt.Color')
include_class('java.awt.Dimension')
include_class('java.awt.Point')
include_class('javax.swing.JFileChooser')
include_class('javax.swing.JOptionPane')
include_class('javax.swing.SwingConstants')


class SudokuApp

  attr_accessor :frame, :solver, :sudoku, :animation

  def initialize
    @labels                      = Hash.new

    @frame                       = JFrame.new
    @frame.title                 ="Massey Sudoku Player Ruby Version(c) Jens Dietrich 2010"

    #label = javax.swing.JButton.new('Hello, World!')
    #frame.getContentPane().add(label)
    @frame.defaultCloseOperation = JFrame::EXIT_ON_CLOSE

    #label.add_action_listener { |event| java.lang.System::exit(0) }

    mainPane                     = JPanel.new(BorderLayout.new())
    @frame.contentPane           = mainPane

    @pane                        = JPanel.new()
    @pane.opaque                 =true
    @pane.border                 =BorderFactory.createEmptyBorder(5, 5, 5, 5)
    @pane.background             =Color::WHITE
    mainPane.add(@pane, BorderLayout::CENTER)


    @pane.setLayout(GridLayout.new(3, 3))

    for i in 1..3 do
      for j in 1..3 do
        initGroup(i, j)
      end
    end

    initActions();
    bPane = JPanel.new(GridLayout.new(3, 1, 3, 3))
    mainPane.add(bPane, BorderLayout::SOUTH)

    @toolbar           = JToolBar.new
    @toolbar.layout    = FlowLayout.new(FlowLayout::CENTER)
    @toolbar.floatable = false
    @toolbar.add(@actExit)
    @toolbar.addSeparator()
    @toolbar.add(@actLoad)
    @toolbar.add(@actNext)
    @toolbar.add(@actPlay)
    @toolbar.add(@actStop)
    switchAnimation(false);

    @slider                 = JSlider.new
    @slider.orientation     =JSlider::HORIZONTAL
    @slider.minimum         =0
    @slider.maximum         =2000
    @slider.value           =1000
    @slider.majorTickSpacing=500
    @slider.minorTickSpacing=100
    @slider.paintLabels     =true
    @slider.paintTicks      =true

    sPane                   = JPanel.new(GridLayout.new(1, 2, 5, 5))
    sPane.border            = BorderFactory.createEmptyBorder(0, 5, 0, 10)
    sPane.add(JLabel.new("animation speed (delay in ms):", JLabel::RIGHT))
    sPane.add(@slider)
    bPane.add(sPane)

    @counterLabel = JLabel.new(" 0")
    sPane         = JPanel.new(GridLayout.new(1, 2, 5, 5))
    sPane.border  = BorderFactory.createEmptyBorder(0, 5, 0, 10)
    sPane.add(JLabel.new("computation steps:", JLabel::RIGHT));
    sPane.add(@counterLabel);
    bPane.add(sPane);
    bPane.add(@toolbar);

    @solver  = SudokuSolver.new
    @sudoku  = Sudoku.new

    @animation = nil

  end

  def start
    dim = java.awt.Toolkit::getDefaultToolkit().getScreenSize();
    @frame.setSize(480, 600);
    @frame.setLocation((dim.width-@frame.size.width)/2, (dim.height-@frame.size.height)/2);
    @frame.visible=true
  end

  def animationDelay
    @slider.value
  end

  def initActions


    @actExit = ExitAction.new("Exit", self)
    @actLoad = LoadAction.new("Load", self)
    @actNext = NextAction.new("Next", self)
    @actPlay = PlayAction.new("Play", self)
    @actStop = StopAction.new("Stop", self)

  end

  def initGroup (column, row)

    p        = JPanel.new
    p.opaque = false
    p.border = BorderFactory.createEmptyBorder(1, 1, 1, 1)

    p.layout = GridLayout.new(3, 3)
    for i in 1..3
      for j in 1..3
        label                     = JLabel.new
        label.opaque              = false
        label.horizontalAlignment = SwingConstants::HORIZONTAL
        label.border              = BorderFactory::createLineBorder(Color::BLACK, 1)
        p.add(label)
        label.text                             ='' # (3*(column-1)+i).to_s+(3*(row-1)+j).to_s
        @labels[[3*(row-1)+j, 3*(column-1)+i]] = label
      end
    end
    @pane.add(p);
  end


end

# not sure why we need this - should be the default
# if removed, script calling this method will fail
public

def setModel(sudoku, resetCounter)
  @sudoku = sudoku
  for cell in sudoku.cells
    label = @labels[[cell.column, cell.row]]
    value = ''
    value = cell.value.to_s unless cell.value==0
    label.text = value unless label==nil
  end

  if @sudoku.solved?
    puts 'done'
    switchAnimation(false);
    @animation.terminate
    JOptionPane.showMessageDialog(nil, "Hello World!")
    #JOptionPane.showMessageDialog(@frame, "Sudoku solved, well done!", "Message", JOptionPane::INFORMATION_MESSAGE)
    #JOptionPane.showMessageDialog(nil, "Error loading file, see console for details", "Error", JOptionPane::ERROR_MESSAGE)
  end

end

def switchAnimation(onOff)
  	@actPlay.enabled = !onOff;
		@actStop.enabled = onOff;
		@actNext.enabled = !onOff;
		@actLoad.enabled = !onOff;
end

#  Anonymous classes are tricky and require either proxy magic
#  http://www.codecommit.com/blog/ruby/jruby-event-proxy.
#  or explicit classes. These classes do not have predefined reference to the instance
#  of the outer class, this must be defined explicitly - we do this here by passing the app
#  parameter

class ExitAction < AbstractAction
  def initialize(label, app)
    super(label)
    @app=app
  end

  def actionPerformed(e)
    java.lang.System::exit(0)
  end
end

class LoadAction < AbstractAction
  def initialize(label, app)
    super(label)
    @app=app
  end

  def actionPerformed(e)
    chooser = JFileChooser.new
    chooser.setCurrentDirectory(java.io.File.new("."))
    returnVal = chooser.showOpenDialog(@app.frame)
    if returnVal == JFileChooser::APPROVE_OPTION
      file   = chooser.selectedFile
      sudoku = SudokuReader.new.read(file.absolutePath);
      @app.setModel(sudoku, true)
      # switchAnimation(false) TODO
    end
  rescue Exception => excJOpt
    JOptionPane.showMessageDialog(@app.frame, "Error loading file, see console for details", "Error", JOptionPane::ERROR_MESSAGE)
    puts exc.message
    puts exc.backtrace.inspect
  end
end

class NextAction < AbstractAction
  def initialize(label, app)
    super(label)
    @app=app
  end

  def actionPerformed(e)
    @app.setModel(@app.solver.getNext(@app.sudoku), false)
  end
end

class PlayAction < AbstractAction
  def initialize(label, app)
    super(label)
    @app=app
  end
  
  def actionPerformed(e)
    @app.animation = Thread.new {@app.switchAnimation(true);loop {@app.setModel(@app.solver.getNext(@app.sudoku), false); sleep @app.animationDelay.to_f/1000.to_f;}}
  end
end

class StopAction < AbstractAction
  def initialize(label, app)
    super(label)
    @app=app
  end

  def actionPerformed(e)
    @app.switchAnimation(false);
    @app.animation.terminate
  end
end